package orch

import (
	"fmt"
	"github.com/fatih/color"
	"github.com/hashicorp/hcl/v2"
	"github.com/ihaiker/tfwrapper/internal/config"
	"github.com/ihaiker/tfwrapper/internal/errors"
	"github.com/ihaiker/tfwrapper/internal/eval"
	"github.com/ihaiker/tfwrapper/internal/util"
	"github.com/sirupsen/logrus"
	"github.com/zclconf/go-cty/cty"
	"path"
	"path/filepath"
	"strings"
)

func initModule(running config.Running, module *config.Module, args []string) hcl.Diagnostics {
	if !commandNeedInit(args) {
		return nil
	}
	_, diags := runTerraformCommand(running, []string{"init"})
	return diags
}

func getTerraformOutputs(cfg *config.Wrapper, moduleName string) (map[string]cty.Value, hcl.Diagnostics) {
	_, module := cfg.FindModule(moduleName)
	if module.Outputs != nil { //一次运行运行一次output
		return module.Outputs, nil
	}
	outputStr, diags := runModule(cfg, moduleName, []string{"output", "-json"})
	if util.HasErrors(diags) {
		return nil, diags
	}

	var err error
	module.Outputs, err = util.TerraformOutputJsonToCtyValueMap(moduleName, []byte(outputStr))
	if err != nil {
		diags = errors.Diagnostics("Convert Terraform output",
			fmt.Errorf("convert terraform output error: %s", err.Error()))
	}
	return module.Outputs, diags
}

func runModule(cfg *config.Wrapper, moduleName string, args []string) (out string, diags hcl.Diagnostics) {
	file, module := cfg.FindModule(moduleName)

	outputs := map[string]cty.Value{}

	for _, dependsOn := range module.DependsOn {
		dependModuleName := strings.TrimPrefix(dependsOn, "module.")
		var moduleOutputs map[string]cty.Value
		if moduleOutputs, diags = getTerraformOutputs(cfg, dependModuleName); util.HasErrors(diags) {
			return
		} else {
			outputs[dependModuleName] = cty.ObjectVal(moduleOutputs)
		}
	}

	logrus.Infof("run in module %s, args: %s",
		color.HiGreenString(moduleName), color.HiYellowString(strings.Join(args, " ")))

	var downloadDir string
	if downloadDir, diags = copySourceToDest(cfg, file); util.HasErrors(diags) {
		return
	}

	running := config.Clone(cfg.Config.Running, cfg.Stack.Running, file.Running)
	running.DownloadDir = util.StringPtr(downloadDir)
	running.WorkingDir = *running.DownloadDir
	running.ModuleName = moduleName
	running.ModuleDir = module.Source
	running.CurrentConfigPath = file.CurrentConfigPath
	running.Terraform = config.MergeTerraform(running.Terraform, module.Terraform)
	running.Generators = config.MergeGenerators(running.Generators, module.Generators)

	ctx, diags := eval.CreateEvalContext(running)
	if util.HasErrors(diags) {
		return
	}
	ctx.SetVariable("module", cty.ObjectVal(outputs))
	attrs, diags := module.Inputs.Body.JustAttributes()
	if util.HasErrors(diags) {
		return
	}

	//auto inputs
	if util.Bool(running.AutoInputs) {
		moduleSourcePath := path.Join(filepath.Dir(running.WrapperConfigPath), module.Source)
		if module.TerraformInputNames, diags = parseTerraformVariables(moduleSourcePath); util.HasErrors(diags) {
			return "", diags
		}
		for _, name := range module.TerraformInputNames {
			for moduleName, value := range outputs {
				if value, has := value.AsValueMap()[name]; has {
					logrus.Debugf("auto input %s from module.%s", color.HiMagentaString(name), color.HiGreenString(moduleName))
					running.Inputs[name] = value
				}
			}
		}
	}

	//赋予module的输入值
	for name, attribute := range attrs {
		if running.Inputs[name], diags = attribute.Expr.Value(ctx.EvalContext); util.HasErrors(diags) {
			return
		}
	}

	//执行generator
	if diags = runGenerator(cfg, running, ctx, module); util.HasErrors(diags) {
		return
	}

	if diags = initModule(running, module, args); util.HasErrors(diags) {
		return
	}

	return runTerraformCommand(running, args)
}
